package com.vdimitr.happycube.puzzle.piece;

import java.util.Arrays;

import com.vdimitr.happycube.puzzle.matrix.MatrixUtils;

/**
 * This class represents a puzzle piece. Every puzzle piece has
 * <ul>
 * <li>A color defined by {@link PuzzlePieceColor}
 * <li>A number (for valid puzzle pieces it should be between 1 and 6
 * (inclusive)). This number is used to find the matrix representation of the
 * puzzle piece
 * <li>A flag to denote whether it is flipped or not defined by
 * {@link PuzzlePieceFlip}
 * <li>A rotation count defined by {@link PuzzlePieceRotation}
 * <li>A 5x5 matrix representation
 * </ul>
 * 
 * @see PuzzlePieceMatrices
 * @see PuzzlePieceColor
 * @see PuzzlePieceFlip
 * 
 * @author vasileios.dimitriadis
 * 
 */
public class PuzzlePiece {

	/**
	 * The color family this puzzle piece belongs to
	 */
	private PuzzlePieceColor color = null;

	/**
	 * Whether it is flipped or not
	 */
	private PuzzlePieceFlip flip = null;

	/**
	 * How many times it is rotated clockwise
	 */
	private PuzzlePieceRotation rotation = null;

	/**
	 * The number of the puzzle piece
	 */
	private int puzzlePieceNumber = -1;

	/**
	 * The matrix representation of the puzzle piece
	 */
	private int matrix[][];

	/**
	 * Default state constructor (no flip, no rotation)
	 * 
	 * @param color
	 *            the color of the puzzle piece
	 * @param puzzlePieceNumber
	 *            the number of the puzzle piece
	 */
	public PuzzlePiece(PuzzlePieceColor color, int puzzlePieceNumber) {
		this.color = color;
		this.puzzlePieceNumber = puzzlePieceNumber;
		this.flip = PuzzlePieceFlip.FALSE;
		this.rotation = PuzzlePieceRotation.NONE;
	}

	/**
	 * An expanded constructor to set initial flip and rotation state of the
	 * puzzle piece
	 * 
	 * @param color
	 *            the color of the puzzle piece
	 * @param puzzlePieceNumber
	 *            the number of the puzzle piece
	 * @param flip
	 *            whether the puzzle piece is flipped or not
	 * @param rotation
	 *            how many times the puzzle piece is rotated clockwise
	 */
	public PuzzlePiece(PuzzlePieceColor color, int puzzlePieceNumber,
			PuzzlePieceFlip flip, PuzzlePieceRotation rotation) {
		this.color = color;
		this.flip = flip;
		this.rotation = rotation;
		this.puzzlePieceNumber = puzzlePieceNumber;
	}

	/**
	 * A copy constructor based on an existing puzzle piece
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to copy properties from
	 */
	public PuzzlePiece(PuzzlePiece puzzlePiece) {
		this.color = puzzlePiece.getColor();
		this.flip = puzzlePiece.getFlip();
		this.rotation = puzzlePiece.getRotation();
		this.puzzlePieceNumber = puzzlePiece.getPuzzlePieceNumber();
		if(puzzlePiece.getMatrix() != null) {
			this.matrix = MatrixUtils.deepCopy(puzzlePiece.getMatrix());
		}
	}

	/**
	 * Set the flip state
	 * 
	 * @param flip
	 *            new flip state
	 */
	public void setFlip(PuzzlePieceFlip flip) {
		this.flip = flip;
	}

	/**
	 * Set the rotation state
	 * 
	 * @param rotation
	 *            new rotation state
	 */
	public void setRotation(PuzzlePieceRotation rotation) {
		this.rotation = rotation;
	}

	/**
	 * 
	 * @return the color of this puzzle piece
	 */
	public PuzzlePieceColor getColor() {
		return this.color;
	}

	/**
	 * 
	 * @return true if it is flipped
	 */
	public boolean isFlipped() {
		return flip.isFlipped();
	}

	/**
	 * 
	 * @return the number of times this puzzle piece has been rotated clockwise
	 */
	public int getRotationCount() {
		return rotation.getRotationCount();
	}

	/**
	 * 
	 * @return its flip state
	 */
	public PuzzlePieceFlip getFlip() {
		return flip;
	}

	/**
	 * 
	 * @return its rotation state
	 */
	public PuzzlePieceRotation getRotation() {
		return rotation;
	}

	/**
	 * 
	 * @return its number
	 */
	public int getPuzzlePieceNumber() {
		return this.puzzlePieceNumber;
	}

	/**
	 * 
	 * @return its matrix representation.
	 */
	public int[][] getMatrix() {
		return matrix;
	}

	/**
	 * Set the matrix representation. The input matrix will be copied.
	 * 
	 * @param matrix
	 *            the new matrix representation
	 */
	public void setMatrix(int[][] matrix) {
		this.matrix = MatrixUtils.deepCopy(matrix);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((color == null) ? 0 : color.hashCode());
		result = prime * result + ((flip == null) ? 0 : flip.hashCode());
		result = prime * result + Arrays.hashCode(matrix);
		result = prime * result + puzzlePieceNumber;
		result = prime * result
				+ ((rotation == null) ? 0 : rotation.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		PuzzlePiece other = (PuzzlePiece) obj;
		if (color == null) {
			if (other.color != null)
				return false;
		} else if (!color.equals(other.color))
			return false;
		if (flip == null) {
			if (other.flip != null)
				return false;
		} else if (!flip.equals(other.flip))
			return false;
		if (!MatrixUtils.matricesAreEquals(matrix, other.matrix))
			return false;
		if (puzzlePieceNumber != other.puzzlePieceNumber)
			return false;
		if (rotation == null) {
			if (other.rotation != null)
				return false;
		} else if (!rotation.equals(other.rotation))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "[" + color + ", " + puzzlePieceNumber + ", " + flip + ", "
				+ rotation + "]";
	}

}
